//
// Created by buk on 8/29/24.
//
#include <future>
#include <fstream>
#include <opencv2/opencv.hpp>
#include "spdlog/spdlog.h"

#include "../../inc/arcsoft_face_sdk.h"
#include "../../inc/amcomdef.h"
#include "../../inc/asvloffscreen.h"
#include "../../inc/merror.h"

#include "ai_service.h"

#define APPID "6v2wZSPMmzbEYQNHqooHFS7iuByHgyAxPoSK7KS9Maz9"
#define SDKKEY "CSL6ULxtXfn6Nu3kaFDwf4F93t4Djfg8NciWXHZAkhTv"

#define NSCALE 16
#define FACENUM    5

#define SafeFree(p) { if ((p)) free(p); (p) = NULL; }
#define SafeArrayDelete(p) { if ((p)) delete [] (p); (p) = NULL; }
#define SafeDelete(p) { if ((p)) delete (p); (p) = NULL; }

namespace ai::facial_recognition {

//时间戳转换为日期格式
    void timestampToTime(char *timeStamp, char *dateTime, int dateTimeSize) {
        time_t tTimeStamp = atoll(timeStamp);
        struct tm *pTm = gmtime(&tTimeStamp);
        strftime(dateTime, dateTimeSize, "%Y-%m-%d %H:%M:%S", pTm);
    }

    //图像颜色格式转换
    int ColorSpaceConversion(MInt32 width, MInt32 height, MInt32 format, MUInt8 *imgData, ASVLOFFSCREEN &offscreen) {
        offscreen.u32PixelArrayFormat = (unsigned int) format;
        offscreen.i32Width = width;
        offscreen.i32Height = height;

        switch (offscreen.u32PixelArrayFormat) {
            case ASVL_PAF_RGB24_B8G8R8:
                offscreen.pi32Pitch[0] = offscreen.i32Width * 3;
                offscreen.ppu8Plane[0] = imgData;
                break;
            case ASVL_PAF_I420:
                offscreen.pi32Pitch[0] = width;
                offscreen.pi32Pitch[1] = width >> 1;
                offscreen.pi32Pitch[2] = width >> 1;
                offscreen.ppu8Plane[0] = imgData;
                offscreen.ppu8Plane[1] = offscreen.ppu8Plane[0] + offscreen.i32Height * offscreen.i32Width;
                offscreen.ppu8Plane[2] = offscreen.ppu8Plane[0] + offscreen.i32Height * offscreen.i32Width * 5 / 4;
                break;
            case ASVL_PAF_NV12:
            case ASVL_PAF_NV21:
                offscreen.pi32Pitch[0] = offscreen.i32Width;
                offscreen.pi32Pitch[1] = offscreen.pi32Pitch[0];
                offscreen.ppu8Plane[0] = imgData;
                offscreen.ppu8Plane[1] = offscreen.ppu8Plane[0] + offscreen.pi32Pitch[0] * offscreen.i32Height;
                break;
            case ASVL_PAF_YUYV:
            case ASVL_PAF_DEPTH_U16:
                offscreen.pi32Pitch[0] = offscreen.i32Width * 2;
                offscreen.ppu8Plane[0] = imgData;
                break;
            case ASVL_PAF_GRAY:
                offscreen.pi32Pitch[0] = offscreen.i32Width;
                offscreen.ppu8Plane[0] = imgData;
                break;
            default:
                return 0;
        }
        return 1;
    }

void convert_hex_str_to_char(const std::string& str, unsigned char * char_array)
    {
        spdlog::debug("convert_hex_str_to_char str.length: {0:d}.", str.length());
        for(size_t i=0; i< str.length(); i += 2)
        {
            std::string byte = str.substr(i, 2);
            char chr = (char)(int)strtol(byte.c_str(), NULL, 16);
            auto j = i / 2;
            char_array[j] = chr;
            // spdlog::debug("{0:d}, {2:d}, {1:c}", j, chr, i);
        }
    }


    std::string convert_unsigned_char_pointer_to_hex(unsigned char * data, int len)
    {
        std::stringstream ss;
        ss << std::uppercase << std::hex << std::setfill('0');
        for (int i = 0; i < len; i++) {
            ss << std::setw(2) << static_cast<unsigned>(data[i]);
        }
        return ss.str();
    }

    AIService::AIService() {

    }

    AIService::~AIService() {


    }

    void AIService::start() {
        this->init_facial_recognition_engine();
    }

    void AIService::stop() {
        //反初始化
        auto res = ASFUninitEngine(handle);
        if (res != MOK)
            printf("ASFUninitEngine fail: %ld\n", res);
        else
            printf("ASFUninitEngine success: %ld\n", res);
    }

    void AIService::init_facial_recognition_engine() {
        printf("\n************* ArcFace SDK Info *****************\n");
        MRESULT res = MOK;
        ASF_ActiveFileInfo activeFileInfo = {0};
        res = ASFGetActiveFileInfo(&activeFileInfo);
        if (res != MOK) {
            printf("ASFGetActiveFileInfo fail: %ld\n", res);
        } else {
            //这里仅获取了有效期时间，还需要其他信息直接打印即可
            char startDateTime[32];
            timestampToTime(activeFileInfo.startTime, startDateTime, 32);
            printf("startTime: %s\n", startDateTime);
            char endDateTime[32];
            timestampToTime(activeFileInfo.endTime, endDateTime, 32);
            printf("endTime: %s\n", endDateTime);
        }

        //SDK版本信息
        const ASF_VERSION version = ASFGetVersion();

        std::cout << "version 的类型是：" << typeid(version).name() << std::endl;

        printf("\nVersion:%s\n", version.Version);
        printf("BuildDate:%s\n", version.BuildDate);
        printf("CopyRight:%s\n", version.CopyRight);

        printf("\n************* Face Recognition *****************\n");

        res = ASFOnlineActivation(APPID, SDKKEY);
        if (MOK != res && MERR_ASF_ALREADY_ACTIVATED != res)
            printf("ASFOnlineActivation fail: %ld\n", res);
        else
            // 90114 SDK已激活
            printf("ASFOnlineActivation success: %ld\n", res);

        //初始化引擎
        MInt32 mask = ASF_FACE_DETECT | ASF_FACERECOGNITION | ASF_AGE | ASF_GENDER | ASF_FACE3DANGLE | ASF_LIVENESS |
                      ASF_IR_LIVENESS;
        res = ASFInitEngine(ASF_DETECT_MODE_IMAGE, ASF_OP_0_ONLY, NSCALE, FACENUM, mask, &handle);
        if (res != MOK)
            printf("ASFInitEngine fail: %ld\n", res);
        else
            printf("ASFInitEngine success: %ld\n", res);
    }

    void AIService::extract_face_feature() {

    }

    void AIService::detect_faces() {
    }


    void AIService::onWebRequest(RequestMessagePtr msg)
    {
        try
        {
            // 读取JPG图片
            auto file_name = msg->web_root + "/" + msg->image_file_name;
            cv::Mat jpgImage0 = cv::imread(file_name, cv::IMREAD_COLOR);
            if (jpgImage0.empty()) {
                std::cout << "图片读取失败" << std::endl;
                return;
            }

            std::cout << "图片读取完成" << std::endl;

            // 创建一个空的NV21格式图片
            int width = jpgImage0.cols;
            int height = jpgImage0.rows;
            std::cout << "original width: " << width << ", height: " << height << std::endl;

            if (width % 2 != 0 || height % 2 != 0) {
                // 调整图片的宽度可以被2整除
                if (width % 4 != 0)
                {
                    auto temp = width % 4;
                    width = width - temp;
                }
                if (height % 2 != 0) height--;
            }

            cv::Mat jpgImage;
            cv::Size newSize(width, height);
            cv::resize(jpgImage0, jpgImage, newSize);
            std::cout << "new width: " << width << ", new height: " << height << std::endl;


            cv::Mat nv21Image(height + height / 2, width, CV_8UC1);
            std::cout << "nv21Image 实例完成" << std::endl;

            // 将BGR图片转换为NV21图片
            cv::cvtColor(jpgImage, nv21Image, cv::ColorConversionCodes::COLOR_RGB2YUV_YV12);
            std::cout << "图像格式转换完成" << std::endl;

            // 转换图片存储格式
            // 计算图片内存长度
            int img_length = nv21Image.total() * nv21Image.channels();
            spdlog::debug("image length: {0:d}." , img_length);
            auto* imageData1 = new unsigned char[img_length]();
            std::memcpy(imageData1, nv21Image.ptr<unsigned char>(0), img_length * sizeof(unsigned char));
            spdlog::debug("内存复制完成");

            ASVLOFFSCREEN offscreen1 = {0};
            ColorSpaceConversion(width, height, ASVL_PAF_NV21, imageData1, offscreen1);

            spdlog::debug("ColorSpaceConversion is done.");

            //第一张人脸
            ASF_MultiFaceInfo detectedFaces1 = {nullptr};
            ASF_SingleFaceInfo SingleDetectedFaces = {0};
            ASF_FaceFeature feature1 = {nullptr};
            ASF_FaceFeature copyfeature1 = {nullptr};

            auto res = ASFDetectFacesEx(handle, &offscreen1, &detectedFaces1);;
            spdlog::debug("ASFDetectFacesEx is done.");

            if (res != MOK && detectedFaces1.faceNum > 0) {
                printf("ASFDetectFaces 1 fail: %ld\n", res);
                return;
            } else {
                printf("ASFDetectFaces 1 success: %ld\n", res);
                printf(" faces: %d\n", detectedFaces1.faceNum);

                if (detectedFaces1.faceNum < 1) {
                    printf(" faces: %d\n", detectedFaces1.faceNum);
                    return;
                }

                SingleDetectedFaces.faceRect.left = detectedFaces1.faceRect[0].left;
                SingleDetectedFaces.faceRect.top = detectedFaces1.faceRect[0].top;
                SingleDetectedFaces.faceRect.right = detectedFaces1.faceRect[0].right;
                SingleDetectedFaces.faceRect.bottom = detectedFaces1.faceRect[0].bottom;
                SingleDetectedFaces.faceOrient = detectedFaces1.faceOrient[0];

                // 单人脸特征提取
                res = ASFFaceFeatureExtractEx(handle, &offscreen1, &SingleDetectedFaces, &feature1);
                if (res != MOK) {
                    printf("ASFFaceFeatureExtractEx 1 fail: %ld\n", res);
                    return;
                } else {
                    printf("ASFFaceFeatureExtractEx 1 success: %ld\n", res);
                    printf(" featureSize: %d\n", feature1.featureSize);
                    printf(" feature: %p\n", feature1.feature);

                    // convert face feature to string
                    std::string str_feature = convert_unsigned_char_pointer_to_hex(feature1.feature, feature1.featureSize);
                    std::cout << str_feature << std::endl;

                     _data_store->save_face_feature(str_feature, feature1.featureSize);

                    //拷贝feature，否则第二次进行特征提取，会覆盖第一次特征提取的数据，导致比对的结果为1
                    copyfeature1.featureSize = feature1.featureSize;
                    copyfeature1.feature = (MByte *) malloc(feature1.featureSize);
                    memset(copyfeature1.feature, 0, feature1.featureSize);
                    memcpy(copyfeature1.feature, feature1.feature, feature1.featureSize);
                }
            }


            //第二张人脸
            // ASVLOFFSCREEN offscreen2 = { 0 };
            // ColorSpaceConversion(width, height, ASVL_PAF_NV21, imageData1, offscreen2);
            //
            // ASF_MultiFaceInfo detectedFaces2 = { 0 };
            // ASF_FaceFeature feature2 = { 0 };
            //
            // res = ASFDetectFacesEx(handle, &offscreen2, &detectedFaces2);
            // if (res != MOK && detectedFaces2.faceNum > 0)
            // {
            //     printf("SFDetectFacesEx 2 fail: %ld\n", res);
            // }
            // else
            // {
            //     SingleDetectedFaces.faceRect.left = detectedFaces2.faceRect[0].left;
            //     SingleDetectedFaces.faceRect.top = detectedFaces2.faceRect[0].top;
            //     SingleDetectedFaces.faceRect.right = detectedFaces2.faceRect[0].right;
            //     SingleDetectedFaces.faceRect.bottom = detectedFaces2.faceRect[0].bottom;
            //     SingleDetectedFaces.faceOrient = detectedFaces2.faceOrient[0];
            //
            //     res = ASFFaceFeatureExtractEx(handle, &offscreen2, &SingleDetectedFaces, &feature2);
            //     if (res != MOK)
            //         printf("ASFFaceFeatureExtractEx 2 fail: %ld\n", res);
            //     else
            //         printf("ASFFaceFeatureExtractEx 2 success: %ld\n", res);
            // }

            // 获取sqlite中保存的所有人脸特征值
            auto face_feature_infos = _data_store->get_all_face_features();
            spdlog::debug("face_feature_infos size: {0:d}.", face_feature_infos.size());
            for (auto info : face_feature_infos)
            {
                spdlog::debug("face_feature_info row_id: {0:d}.", info.row_id);

                ASF_FaceFeature existed_feature = {nullptr};
                existed_feature.featureSize = info.face_feature_size;
                existed_feature.feature = (MByte *) malloc(info.face_feature_size);
                memset(existed_feature.feature, 0, existed_feature.featureSize);
                // memcpy(existed_feature.feature, info.feature, info.face_feature_size);

                convert_hex_str_to_char(info.face_feature, existed_feature.feature);
                spdlog::debug("convert_hex_str_to_char: {0:d}.", info.row_id);


                // convert face feature to string
                std::string str_feature_confirmed = convert_unsigned_char_pointer_to_hex(existed_feature.feature, existed_feature.featureSize);
                if (str_feature_confirmed != info.face_feature)
                {
                    spdlog::error("Original feature: {0:s}.", str_feature_confirmed);
                    spdlog::error("Convert  feature: {0:s}.", info.face_feature);
                    return;
                }



                // 单人脸特征比对
                MFloat confidenceLevel;
                res = ASFFaceFeatureCompare(handle, &copyfeature1, &existed_feature, &confidenceLevel);
                if (res != MOK)
                    printf("ASFFaceFeatureCompare fail: %ld\n", res);
                else
                    printf("%d ASFFaceFeatureCompare success: %lf\n", info.row_id,  confidenceLevel);
            }


            printf("\n************* Face Process *****************\n");
            //设置活体置信度 SDK内部默认值为 IR：0.7  RGB：0.5（无特殊需要，可以不设置）
            ASF_LivenessThreshold threshold = { 0 };
            threshold.thresholdmodel_BGR = 0.5;
            threshold.thresholdmodel_IR = 0.7;
            res = ASFSetLivenessParam(handle, &threshold);
            if (res != MOK)
                printf("ASFSetLivenessParam fail: %ld\n", res);
            else
                printf("RGB Threshold: %f\nIR Threshold: %f\n", threshold.thresholdmodel_BGR, threshold.thresholdmodel_IR);


            // 人脸信息检测
            MInt32 processMask = ASF_AGE | ASF_GENDER | ASF_FACE3DANGLE | ASF_LIVENESS;
            res = ASFProcessEx(handle, &offscreen1, &detectedFaces1, processMask);
            if (res != MOK)
                printf("ASFProcessEx fail: %ld\n", res);
            else
                printf("ASFProcessEx success: %ld\n", res);

            // 获取年龄
            ASF_AgeInfo ageInfo = { nullptr };
            res = ASFGetAge(handle, &ageInfo);
            if (res != MOK)
                printf("ASFGetAge fail: %ld\n", res);
            else
                printf("First face age: %d\n", ageInfo.ageArray[0]);

            // 获取性别
            ASF_GenderInfo genderInfo = { nullptr };
            res = ASFGetGender(handle, &genderInfo);
            if (res != MOK)
                printf("ASFGetGender fail: %ld\n", res);
            else
                printf("First face gender: %d\n", genderInfo.genderArray[0]);

            // 获取3D角度
            ASF_Face3DAngle angleInfo = { nullptr };
            res = ASFGetFace3DAngle(handle, &angleInfo);
            if (res != MOK)
                printf("ASFGetFace3DAngle fail: %ld\n", res);
            else
                printf("First face 3dAngle: roll: %lf yaw: %lf pitch: %lf\n", angleInfo.roll[0], angleInfo.yaw[0], angleInfo.pitch[0]);
        } catch (std::exception& ex)
        {
            spdlog::error(ex.what());
        }

    }

    void AIService::set_data_store(DataStore * data_store) {
        _data_store = data_store;
    }

}